package com.cyt.work.association.service.impl;

import cn.hutool.core.date.DateUtil;
import com.cyt.base.exception.base.BaseException;
import com.cyt.base.model.dto.ReturnResult;
import com.cyt.base.redis.RedisService;
import com.cyt.base.util.*;
import com.cyt.sys.service.impl.BaseServiceImpl;
import com.cyt.work.association.dao.WxAssociationNewDetailDao;
import com.cyt.work.association.dao.WxAssociationSignDao;
import com.cyt.work.association.model.po.WxAssociationSign;
import com.cyt.work.association.model.vo.WxAssociationNewDetailVoC;
import com.cyt.work.association.model.vo.WxAssociationSignVo;
import com.cyt.work.association.model.vo.WxAssociationSignVoC;
import com.cyt.work.association.service.WxAssociationSignService;
import com.cyt.wx.model.po.WxEducationUser;
import com.cyt.wx.model.po.WxMember;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

/**
 * 社团招新管理-社团报名Service层实现类
 *
 * @author ding xu
 * @date 2019-9-17
 */
@Service("wxAssociationSignService")
public class WxAssociationSignServiceImpl extends BaseServiceImpl<WxAssociationSign> implements WxAssociationSignService {

    private static Logger log = LoggerFactory.getLogger(WxAssociationSignServiceImpl.class);

    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Resource
    private RedisService redisService;
    @Resource
    private WxAssociationSignDao wxAssociationSignDao;
    @Resource
    private WxAssociationNewDetailDao wxAssociationNewDetailDao;

    @Autowired
    public void setBaseDao(WxAssociationSignDao wxAssociationSignDao) {
        this.baseDao = wxAssociationSignDao;
    }

    @Override
    public ReturnResult addOrUpdate(WxAssociationSignVo wxAssociationSignVo,WxMember wxMember,WxEducationUser wxEducationUser) {
        //判断报名条件
//        WxAssociationNewDetailSignVo wxAssociationNewDetailSignVo=new WxAssociationNewDetailSignVo();
//        wxAssociationNewDetailSignVo.setAssociationId(wxAssociationSignVo.getAssociationId());
//        wxAssociationNewDetailSignVo.setAssociationNewId(wxAssociationSignVo.getAssociationNewId());
//        wxAssociationNewDetailSignVo.setAssociationStuId(wxEducationUser.getId());
//        WxAssociationNewDetailSignVo wxAssociationNewDetailSignVo1=wxAssociationNewDetailDao.selectVoOneSign(wxAssociationNewDetailSignVo);
//        //判断是否已超出本次招新单人最大报名数
//        if(Integer.parseInt(wxAssociationNewDetailSignVo1.getTotalSign())>=Integer.parseInt(wxAssociationNewDetailSignVo1.getMaxTotalSign())){
//            return new ReturnResult(ErrorCode.CODE_1, BaseConstant.ASSOCITATION_SIGN_PMAX_OUT);
//        }
//        //判断是否已超出社团招新人数总数
//        if(Integer.parseInt(wxAssociationNewDetailSignVo1.getStuNumber())>=Integer.parseInt(wxAssociationNewDetailSignVo1.getTotalNumber())){
//            return new ReturnResult(ErrorCode.CODE_1, BaseConstant.ASSOCITATION_SIGN_MAXOUT);
//        }
        try{
            if (StringUtils.isBlank(wxAssociationSignVo.getId())) {
//            WxAssociationSign wxAssociationSign=new WxAssociationSign();
//            wxAssociationSign.setId(BaseUtil.uId());
//            wxAssociationSign.setApplyId(wxAssociationSignVo.getApplyId());
//            wxAssociationSign.setAssociationId(wxAssociationSignVo.getAssociationId());
//            wxAssociationSign.setAssociationNewId(wxAssociationSignVo.getAssociationNewId());
//            wxAssociationSign.setAssociationUserId(wxEducationUser.getId());
//            wxAssociationSign.setSignTime(new Date());
//            wxAssociationSign.setStatus(BaseConstant.ASSOCITATION_SIGN_STATE_SUCCESSD);
//            wxAssociationSign.setRemoveTag(DictCode.BASE_REMOVE_TAG_NORMAL);
              wxAssociationSignVo.setId(BaseUtil.uId());
              wxAssociationSignVo.setAssociationUserId(wxEducationUser.getId());

                int id= wxAssociationSignDao.insertAssSign(wxAssociationSignVo);
                if (id <= 0) {
                    return new ReturnResult(ErrorCode.CODE_1, BaseConstant.ASSOCITATION_SIGN_MAXOUT);
                } else {
//                WxAssociationNewDetailVo wxAssociationNewDetailVo= AssassicationDetailUtils.getAssasicationDetails(wxAssociationSignVo.getAssociationNewId()+wxAssociationSignVo.getAssociationId());
//                wxAssociationNewDetailVo.setStuNumber((Integer.parseInt(wxAssociationNewDetailVo.getStuNumber())+1)+"");
//                CacheUtils.put(AssassicationDetailUtils.CACHE_ASSNEW_MAP,wxAssociationSignVo.getAssociationNewId()+wxAssociationSignVo.getAssociationId(),wxAssociationNewDetailVo);
                    return new ReturnResult(ErrorCode.CODE_0, BaseConstant.ASSOCITATION_SIGN_SUCCESSD);
                }
            }else{
                return new ReturnResult(ErrorCode.CODE_1,BaseConstant.ASSOCITATION_SIGN_MAXOUT);
            }
            }
            catch (Exception e){
                log.error(e.getMessage());
                return new ReturnResult(ErrorCode.CODE_1,BaseConstant.ASSOCITATION_SIGN_MAXOUT);
            }
    }

    @Override
    public ReturnResult quitAssociation(WxAssociationSignVo wxAssociationSignVo,WxMember wxMember,WxEducationUser wxEducationUser) {
            WxAssociationSign wxAssociationSign=new WxAssociationSign();
            wxAssociationSign.setAssociationUserId(wxEducationUser.getId());
            wxAssociationSign.setId(wxAssociationSignVo.getId());
            wxAssociationSign.setAssociationNewId(wxAssociationSignVo.getAssociationNewId());
            wxAssociationSign.setAssociationId(wxAssociationSignVo.getAssociationId());
            int id= wxAssociationSignDao.delete(wxAssociationSign);
            if (id <= 0) {
                return new ReturnResult(ErrorCode.CODE_1, BaseConstant.ASSOCITATION_SIGN_QUIT_ERROR);
            }else{
//                WxAssociationNewDetailVo wxAssociationNewDetailVo= AssassicationDetailUtils.getAssasicationDetails(wxAssociationSignVo.getAssociationNewId()+wxAssociationSignVo.getAssociationId());
//                wxAssociationNewDetailVo.setStuNumber((Integer.parseInt(wxAssociationNewDetailVo.getStuNumber())-1)+"");
//                CacheUtils.put(AssassicationDetailUtils.CACHE_ASSNEW_MAP,wxAssociationSignVo.getAssociationNewId()+wxAssociationSignVo.getAssociationId(),wxAssociationNewDetailVo);
                return new ReturnResult(ErrorCode.CODE_0, BaseConstant.ASSOCITATION_SIGN_QUIT_SUCCESSD);
            }
    }

    @Override
    public List<WxAssociationSign> getByAssIdAndAssNewId(WxAssociationSignVo wxAssociationSignVo) {
        WxAssociationSign wxAssociationSign=new WxAssociationSign();
        wxAssociationSign.setAssociationUserId(wxAssociationSignVo.getAssociationUserId());
        wxAssociationSign.setAssociationId(wxAssociationSignVo.getAssociationId());
        wxAssociationSign.setAssociationNewId(wxAssociationSignVo.getAssociationNewId());
        return wxAssociationSignDao.select(wxAssociationSign);
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public ReturnResult associationNewSign(String userId, String applyId, String associationId, String associationNewId, String associationNewDetailId) {
        // 1、查询社团中是否已招满
        Long max = redisService.decrBy(AssociationConstant.WX_ASSOCIATION_NEW_DETAIL_NUM + associationNewDetailId, 1);
        if (max < 0) {
            // 招新人数已满
            log.info("学生主键为{}报名社团失败，招新关联社团表主键为{}，但是招新人数已满", userId, associationNewDetailId);
            redisService.increment(AssociationConstant.WX_ASSOCIATION_NEW_DETAIL_NUM + associationNewDetailId, 1);
            return new ReturnResult(ErrorCode.CODE_1, BaseConstant.ASSOCITATION_SIGN_MAX);
        }
        // 2、判断本身是否已报名社团，报名社团总数
        // 获取学生本身报名信息，报名数及报名社团。学生需要关联招新，单次招新中的信息
        if (redisService.exists(AssociationConstant.WX_ASSOCIATION_NEW_STU_DETAIL + associationNewDetailId +  "_" + userId)) {
            redisService.increment(AssociationConstant.WX_ASSOCIATION_NEW_DETAIL_NUM + associationNewDetailId, 1);
            log.info("学生主键为{}报名社团失败，招新关联社团表主键为{}，但是学生已在队列中，请联系管理员查看", userId, associationNewDetailId);
            return new ReturnResult(ErrorCode.CODE_1, BaseConstant.ASSOCITATION_SIGN_LOADING);
        }
        // 单次招新学生报名数目
        if (redisService.decrBy(AssociationConstant.WX_ASSOCIATION_NEW_STU_NUM + associationNewId + "_" + userId, 1) < 0) {
            redisService.increment(AssociationConstant.WX_ASSOCIATION_NEW_DETAIL_NUM + associationNewDetailId, 1);
            redisService.increment(AssociationConstant.WX_ASSOCIATION_NEW_STU_NUM + associationNewId + "_" + userId, 1);
            log.info("学生主键为{}报名社团失败，招新关联社团表主键为{}，该学生已报社团数超过最大报名数", userId, associationNewDetailId);
            return new ReturnResult(ErrorCode.CODE_1, BaseConstant.ASSOCITATION_SIGN_PMAX_OUT);
        }
        // 报名，1、直接报名
        Date now = new Date();
        WxAssociationSign wxAssociationSign = new WxAssociationSign();
        wxAssociationSign.setId(BaseUtil.uId());
        wxAssociationSign.setApplyId(applyId);
        wxAssociationSign.setAssociationId(associationId);
        wxAssociationSign.setAssociationNewId(associationNewId);
        wxAssociationSign.setAssociationDetailId(associationNewDetailId);
        wxAssociationSign.setAssociationUserId(userId);
        wxAssociationSign.setSignTime(now);
        wxAssociationSign.setStatus(BaseConstant.ASSOCITATION_SIGN_STATE_SUCCESSD);
        wxAssociationSign.setRemoveTag(DictCode.BASE_REMOVE_TAG_NORMAL);
        try {
            int flag = wxAssociationSignDao.insert(wxAssociationSign);
            if (flag <= 0) {
                // 回滚redis操作
                redisService.increment(AssociationConstant.WX_ASSOCIATION_NEW_DETAIL_NUM + associationNewDetailId, 1);
                redisService.decrBy(AssociationConstant.WX_ASSOCIATION_NEW_STU_NUM + associationNewId + "_" + userId, 1);
            }
        } catch (Exception e) {
            redisService.increment(AssociationConstant.WX_ASSOCIATION_NEW_DETAIL_NUM + associationNewDetailId, 1);
            redisService.decrBy(AssociationConstant.WX_ASSOCIATION_NEW_STU_NUM + associationNewId + "_" + userId, 1);
            // 回滚redis操作
            log.info("数据库保存学生主键{}，入团{}异常，回滚操作", userId, associationNewDetailId);
            throw new BaseException(ErrorCode.CODE_1, "数据库出现问题，请等待处理");
        }
        // 写入缓存中
        // 3、报名社团，总社团数已减少，学生报名社团已增加，加入学生详情
        redisService.set(AssociationConstant.WX_ASSOCIATION_NEW_STU_DETAIL + associationNewDetailId + "_" + userId, associationNewDetailId);
        log.info("学生主键为{}报名社团成功，招新关联社团表主键为{}，时间是：{}", userId, associationNewDetailId, DateUtil.formatDateTime(now));
        return new ReturnResult(ErrorCode.CODE_0, BaseConstant.ASSOCITATION_SIGN_SUCCESSD);
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public ReturnResult associationNewSignT(String userId, String applyId, String associationId, String associationNewId, String associationNewDetailId) {
        // 1、查询社团中是否已招满
        Long max = redisService.decrBy(AssociationConstant.WX_ASSOCIATION_NEW_DETAIL_NUM + associationNewDetailId, 1);
        if (max < 0) {
            // 招新人数已满
            log.info("学生主键为{}报名社团失败，招新关联社团表主键为{}，但是招新人数已满", userId, associationNewDetailId);
            redisService.increment(AssociationConstant.WX_ASSOCIATION_NEW_DETAIL_NUM + associationNewDetailId, 1);
            return new ReturnResult(ErrorCode.CODE_1, BaseConstant.ASSOCITATION_SIGN_MAX);
        }
        // 2、判断本身是否已报名社团，报名社团总数
        // 获取学生本身报名信息，报名数及报名社团。学生需要关联招新，单次招新中的信息
        if (redisService.exists(AssociationConstant.WX_ASSOCIATION_NEW_STU_DETAIL + associationNewDetailId +  "_" + userId)) {
            redisService.increment(AssociationConstant.WX_ASSOCIATION_NEW_DETAIL_NUM + associationNewDetailId, 1);
            log.info("学生主键为{}报名社团失败，招新关联社团表主键为{}，但是学生已在队列中，请联系管理员查看", userId, associationNewDetailId);
            return new ReturnResult(ErrorCode.CODE_1, BaseConstant.ASSOCITATION_SIGN_LOADING);
        }
        // 单次招新学生报名数目
        if (redisService.decrBy(AssociationConstant.WX_ASSOCIATION_NEW_STU_NUM + associationNewId + "_" + userId, 1) < 0) {
            redisService.increment(AssociationConstant.WX_ASSOCIATION_NEW_DETAIL_NUM + associationNewDetailId, 1);
            redisService.increment(AssociationConstant.WX_ASSOCIATION_NEW_STU_NUM + associationNewId + "_" + userId, 1);
            log.info("学生主键为{}报名社团失败，招新关联社团表主键为{}，该学生已报社团数超过最大报名数", userId, associationNewDetailId);
            return new ReturnResult(ErrorCode.CODE_1, BaseConstant.ASSOCITATION_SIGN_PMAX_OUT);
        }
        // 报名，2、使用多线程
        taskExecutor.execute(() -> {
            Date now = new Date();
            WxAssociationSign wxAssociationSign = new WxAssociationSign();
            wxAssociationSign.setId(BaseUtil.uId());
            wxAssociationSign.setApplyId(applyId);
            wxAssociationSign.setAssociationId(associationId);
            wxAssociationSign.setAssociationNewId(associationNewId);
            wxAssociationSign.setAssociationDetailId(associationNewDetailId);
            wxAssociationSign.setAssociationUserId(userId);
            wxAssociationSign.setSignTime(now);
            wxAssociationSign.setStatus(BaseConstant.ASSOCITATION_SIGN_STATE_SUCCESSD);
            wxAssociationSign.setRemoveTag(DictCode.BASE_REMOVE_TAG_NORMAL);
            try {
                int flag = wxAssociationSignDao.insert(wxAssociationSign);
                if (flag <= 0) {
                    // 回滚redis操作
                    redisService.increment(AssociationConstant.WX_ASSOCIATION_NEW_DETAIL_NUM + associationNewDetailId, 1);
                    redisService.decrBy(AssociationConstant.WX_ASSOCIATION_NEW_STU_NUM + associationNewId + "_" + userId, 1);
                }
            } catch (Exception e) {
                redisService.increment(AssociationConstant.WX_ASSOCIATION_NEW_DETAIL_NUM + associationNewDetailId, 1);
                redisService.decrBy(AssociationConstant.WX_ASSOCIATION_NEW_STU_NUM + associationNewId + "_" + userId, 1);
                // 回滚redis操作
                log.info("数据库保存学生主键{}，入团{}异常，回滚操作", userId, associationNewDetailId);
                throw new BaseException(ErrorCode.CODE_1, "数据库出现问题，请等待处理");
            }
            // 写入缓存中
            // 3、报名社团，总社团数已减少，学生报名社团已增加，加入学生详情
            redisService.set(AssociationConstant.WX_ASSOCIATION_NEW_STU_DETAIL + associationNewDetailId + "_" + userId, associationNewDetailId);
            log.info("学生主键为{}报名社团成功，招新关联社团表主键为{}，时间是：{}", userId, associationNewDetailId, DateUtil.formatDateTime(now));
        });
        return new ReturnResult(ErrorCode.CODE_0, BaseConstant.ASSOCITATION_SIGN_SUCCESSD);
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public ReturnResult associationNewSignR(String userId, String applyId, String associationId, String associationNewId, String associationNewDetailId) {
        // 1、查询社团中是否已招满
        Long max = redisService.decrBy(AssociationConstant.WX_ASSOCIATION_NEW_DETAIL_NUM + associationNewDetailId, 1);
        if (max < 0) {
            // 招新人数已满
            log.info("学生主键为{}报名社团失败，招新关联社团表主键为{}，但是招新人数已满", userId, associationNewDetailId);
            redisService.increment(AssociationConstant.WX_ASSOCIATION_NEW_DETAIL_NUM + associationNewDetailId, 1);
            return new ReturnResult(ErrorCode.CODE_1, BaseConstant.ASSOCITATION_SIGN_MAX);
        }
        // 2、判断本身是否已报名社团，报名社团总数
        // 查询学生是否已报名社团
        if (redisService.exists(AssociationConstant.WX_ASSOCIATION_NEW_STU_DETAIL + associationNewDetailId +  "_" + userId)) {
            redisService.increment(AssociationConstant.WX_ASSOCIATION_NEW_DETAIL_NUM + associationNewDetailId, 1);
            log.info("学生主键为{}报名社团失败，招新关联社团表主键为{}，但是学生已在队列中，请联系管理员查看", userId, associationNewDetailId);
            return new ReturnResult(ErrorCode.CODE_1, BaseConstant.ASSOCITATION_SIGN_LOADING);
        }
        // 单次招新学生报名数目
        if (redisService.decrBy(AssociationConstant.WX_ASSOCIATION_NEW_STU_NUM + associationNewId + "_" + userId, 1) < 0) {
            redisService.increment(AssociationConstant.WX_ASSOCIATION_NEW_DETAIL_NUM + associationNewDetailId, 1);
            redisService.increment(AssociationConstant.WX_ASSOCIATION_NEW_STU_NUM + associationNewId + "_" + userId, 1);
            log.info("学生主键为{}报名社团失败，招新关联社团表主键为{}，该学生已报社团数超过最大报名数", userId, associationNewDetailId);
            return new ReturnResult(ErrorCode.CODE_1, BaseConstant.ASSOCITATION_SIGN_PMAX_OUT);
        }
        // 写入缓存中
        // 3、报名社团，总社团数已减少，学生报名社团已增加，加入学生详情
        redisService.set(AssociationConstant.WX_ASSOCIATION_NEW_STU_DETAIL + associationNewDetailId + "_" + userId, associationNewDetailId);
        // 报名，3、使用队列
        String signId = BaseUtil.uId();
        HashMap<String, String> map = new HashMap<>();
        map.put("signId", signId);
        map.put("applyId", applyId);
        map.put("associationId", associationId);
        map.put("associationNewId", associationNewId);
        map.put("associationNewDetailId", associationNewDetailId);
        map.put("userId", userId);
        rabbitTemplate.convertAndSend("que_sign_key_t", map);
        return new ReturnResult(signId);
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public ReturnResult associationNewQuit(String userId, String associationNewId, String associationNewDetailId, String signId) {
        if (StringUtils.isEmpty(signId)) {
            return new ReturnResult(ErrorCode.CODE_1, "未查询到报名记录，请联系管理员");
        }
        // 1、判断社团是否在缓存中
        if (!redisService.exists(AssociationConstant.WX_ASSOCIATION_NEW_STU_DETAIL + associationNewDetailId + "_" + userId)) {
            log.info("学生主键为{}退出社团失败，招新关联社团表主键为{}，未查询到在缓存中报名记录，请联系管理员", userId, associationNewDetailId);
            return new ReturnResult(ErrorCode.CODE_1, "未查询到在缓存中报名记录，请联系管理员");
        }
        // 2、删除本身社团
        int flag = wxAssociationSignDao.deleteByPrimaryKey(signId);
        if (flag <= 0) {
            return new ReturnResult(ErrorCode.CODE_1, BaseConstant.ASSOCITATION_SIGN_QUIT_ERROR);
        }
        // 3、在缓存中本身社团数加1,社团总数加1
        redisService.del(AssociationConstant.WX_ASSOCIATION_NEW_STU_DETAIL + associationNewDetailId + "_" + userId);
        redisService.increment(AssociationConstant.WX_ASSOCIATION_NEW_STU_NUM + associationNewId + "_" + userId, 1);
        redisService.increment(AssociationConstant.WX_ASSOCIATION_NEW_DETAIL_NUM + associationNewDetailId, 1);
        log.info("学生主键为{}退出社团成功，招新关联社团表主键为{}，时间是：{}", userId, associationNewDetailId, DateUtil.formatDateTime(new Date()));
        return new ReturnResult(ErrorCode.CODE_0, BaseConstant.ASSOCITATION_SIGN_QUIT_SUCCESSD);
    }

    @Override
    public ReturnResult findVoHasSignPage(String associationDetailId, int pageSize, int pageNum) {
        PageHelper.startPage(pageNum, pageSize);
        List<WxAssociationSignVoC> list = wxAssociationSignDao.selectHasSignVoList(associationDetailId);
        PageInfo<WxAssociationSignVoC> page = new PageInfo<>(list);
        return new ReturnResult(page.getList(), page.getTotal(), page.isHasNextPage());
    }

    @Override
    public ReturnResult findDetailVoByUserPage(String userId, int pageSize, int pageNum) {
        PageHelper.startPage(pageNum, pageSize);
        List<WxAssociationNewDetailVoC> list = wxAssociationSignDao.selectDetailVoByUserList(userId);
        PageInfo<WxAssociationNewDetailVoC> page = new PageInfo<>(list);
        return new ReturnResult(page.getList(), page.getTotal(), page.isHasNextPage());
    }
}